package main

import (
	"bytes"
	"compress/zlib"
	"errors"
	"hash/crc32"
	"io"
)

/************协议格式************************************

WTAS协议是交易系统与管理客户端/会员系统之间的通信协议，由报文头 + 报文体两部分组成
标识	            类型	字节数	内容	说明
HeadTag	        byte	1	0xFF	头标记
Length	        uint	4		包总长度，包括头长度
FunCode	        uint	4		功能号（为0代表心跳，心跳数据体长度为0）
SessionId	    uint	4		会话ID，由前置(代理)维护
Mode	        byte	1		内容类型，0:ProtoBuff，1:Json，2:Zip + ProtBuff，…
Version	        byte	1		版本号
SerialNumber	uint	4		通讯报文序号
数据体				业务结构体，ProtoBuf
CRC	            byte	4		报文校验和
FootTag	        byte	1	0x00	尾标记

数据体前,包头长度=19, 空包长度=24
#define MAXKEY    "B0FB83E39A5EBFAABE471362A58393FF"
#define TRANSKEY  "F7A72DE7D6264530F01BA49BC73EB873"
*******************************************************/

var (
	cryptObj *MICryptObj
)

type MiPacket struct {
	Length       uint32
	FunCode      uint32
	SessionId    uint32
	Mode         uint32
	SerialNumber uint32
	MsgData      []byte
	CRC          uint32
}

func printError(label string, err error) {
	//fmt.Println(label, err)
}

func InitEncrypto() error {
	cryptObj = NewMICryptObj()
	return nil
}

func (p *MiPacket) EnPack() []byte {
	p.Length = uint32(len(p.MsgData)) + 24

	buf := make([]byte, 0)
	buf = append(buf, byte(0xFF))
	buf = append(buf, UintToBytes(p.Length)...)
	buf = append(buf, UintToBytes(p.FunCode)...)
	buf = append(buf, UintToBytes(p.SessionId)...)
	buf = append(buf, byte(0))
	buf = append(buf, byte(0))
	buf = append(buf, UintToBytes(p.SerialNumber)...)
	buf = append(buf, p.MsgData...)
	p.CRC = crc32.ChecksumIEEE(buf[0:19])
	buf = append(buf, UintToBytes(p.CRC)...)
	buf = append(buf, byte(0))

	return buf
}

func (p *MiPacket) UnPackHead(buf []byte) error {
	if len(buf) != 19 {
		return errors.New("header len error")
	}

	if buf[0] != 0xFF {
		return errors.New("header flag error")
	}

	p.Length = BytesToUint32(buf[1:5])
	p.FunCode = BytesToUint32(buf[5:9])
	p.SessionId = BytesToUint32(buf[9:13])
	p.SerialNumber = BytesToUint32(buf[15:19])
	p.Mode = uint32(buf[13])

	return nil
}

func MakePacket(funCode, sessionId, serialNum uint32, msg []byte, bCrypto bool) ([]byte, error) {
	p := new(MiPacket)
	p.FunCode = funCode
	p.SessionId = sessionId
	p.SerialNumber = serialNum
	if bCrypto && len(msg) > 0 {
		buf := cryptObj.TransEncrypt(msg)
		p.MsgData = buf
		return p.EnPack(), nil
	}

	p.MsgData = msg
	return p.EnPack(), nil
}

func DecodeBody(mode uint32, msg []byte) ([]byte, error) {

	// mode, 0:ProtoBuff，1:Json，2:Zip + ProtBuff
	if mode == 2 {
		b := bytes.NewReader(msg)
		r, err := zlib.NewReader(b)
		if err != nil {
			return nil, err
		}
		var out bytes.Buffer
		io.Copy(&out, r)
		r.Close()
		msg = out.Bytes()
	}

	return cryptObj.TransDecrypt(msg)
}
